package com.xd.common.util;

import cn.hutool.crypto.KeyUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.alibaba.fastjson.JSON;
import com.alipay.api.domain.ChannelInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.tomcat.util.http.fileupload.IOUtils;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

@Slf4j
public class RSAUtils {
    // 算法类别
    public static final String RSA_ALGORITHM = "RSA";
    public static final String CHARSET = "UTF-8";
    // 算法位数
    private final static Integer KEY_SIZE = 2048;

    /**
     * 生成公私钥对
     */
    public static Map<String, String> getPublicPrivateKey() {
        Map<String, String> pubPriKey = new HashMap<>();
        KeyPair keyPair = KeyUtil.generateKeyPair(RSA_ALGORITHM, KEY_SIZE);
        String publicKeyStr = java.util.Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded());
        String privateKeyStr = java.util.Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded());
        pubPriKey.put("publicKey", publicKeyStr);
        pubPriKey.put("privateKey", privateKeyStr);
        return pubPriKey;
    }

    /*
     * 得到私钥
     * @param privateKey 密钥字符串（经过base64编码）
     * @throws Exception
     */
    public static RSAPrivateKey getPrivateKey(String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
        //通过PKCS#8编码的Key指令获得私钥对象
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey));
        RSAPrivateKey key = (RSAPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec);
        return key;
    }

    /**
     * 得到公钥
     *
     * @param publicKey 密钥字符串（经过base64编码）
     * @throws Exception
     */
    public static RSAPublicKey getPublicKey(String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
        //通过X509编码的Key指令获得公钥对象
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKey));
        RSAPublicKey key = (RSAPublicKey) keyFactory.generatePublic(x509KeySpec);
        return key;
    }


    /*
     * 公钥加密
     * @param data
     * @param publicKey
     * @return
     * encodeBase64String：输出的 Base64 字符串包含 + 和 / 字符，适合在普通文本中传输。
     * encodeBase64URLSafeString：输出的 Base64 字符串使用 - 和 _ 字符，适合在 URL 中传输。
     */
    public static String encrypt(String str, String publicKey) {
        // base64编码的公钥
        byte[] decoded = Base64.decodeBase64(publicKey);
        RSAPublicKey pubKey;
        String outStr = null;

        try {
            pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, pubKey);
            outStr = Base64.encodeBase64String(cipher.doFinal(str.getBytes(StandardCharsets.UTF_8)));
        } catch (InvalidKeySpecException | BadPaddingException | IllegalBlockSizeException | InvalidKeyException |
                 NoSuchPaddingException | NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        // RSA加密
        return outStr;
    }

    public static String publicEncrypt(String data, String publicString) {
        try {
            RSAPublicKey publicKey = getPublicKey(publicString);
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            return Base64.encodeBase64String(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET), publicKey.getModulus().bitLength()));
        } catch (Exception e) {
            throw new RuntimeException("RSA加密失败,密钥错误");
            //throw new RuntimeException("加密字符串[" + data + "]时遇到异常,公钥为"+publicString, e);
        }
    }

    //rsa切割解码  , ENCRYPT_MODE 加密数据   ,DECRYPT_MODE 解密数据
    private static byte[] rsaSplitCodec(Cipher cipher, int opmode, byte[] datas, int keySize) {
        int maxBlock = 0;  //最大块
        if (opmode == Cipher.DECRYPT_MODE) {
            maxBlock = keySize / 8;
        } else {
            maxBlock = keySize / 8 - 11;
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] buff;
        int i = 0;
        try {
            while (datas.length > offSet) {
                if (datas.length - offSet > maxBlock) {
                    //可以调用以下的doFinal（）方法完成加密或解密数据：
                    buff = cipher.doFinal(datas, offSet, maxBlock);
                } else {
                    buff = cipher.doFinal(datas, offSet, datas.length - offSet);
                }
                out.write(buff, 0, buff.length);
                i++;
                offSet = i * maxBlock;
            }
        } catch (Exception e) {
            throw new RuntimeException("加解密阀值为[" + maxBlock + "]的数据时发生异常", e);
        }
        byte[] resultDatas = out.toByteArray();
        IOUtils.closeQuietly(out);
        return resultDatas;
    }
    public static String decryptFullText(String fullEncryptedData, String privateKeyStr) {
        try {
            RSAPrivateKey privateKey = getPrivateKey(privateKeyStr);
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, privateKey);

            byte[] encryptedBytes = Base64.decodeBase64(fullEncryptedData);
            // 如果密文超过 RSA 解密块大小，强制分段
            int maxBlockSize = privateKey.getModulus().bitLength() / 8;
            if (encryptedBytes.length <= maxBlockSize) {
                return new String(cipher.doFinal(encryptedBytes), CHARSET);
            } else {
                // 手动分段解密
                ByteArrayOutputStream output = new ByteArrayOutputStream();
                int offset = 0;
                while (offset < encryptedBytes.length) {
                    int length = Math.min(encryptedBytes.length - offset, maxBlockSize);
                    byte[] decryptedBlock = cipher.doFinal(encryptedBytes, offset, length);
                    output.write(decryptedBlock, 0, decryptedBlock.length);
                    offset += length;
                }
                return output.toString(CHARSET);
            }
        } catch (Exception e) {
            throw new RuntimeException("解密失败", e);
        }
    }
    /*
     * 解密
     * */
    public static String privateDecrypt(String data, String privateString) {
        try {
            RSAPrivateKey privateKey = getPrivateKey(privateString);
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.decodeBase64(data), privateKey.getModulus().bitLength()), "UTF-8");
        } catch (Exception e) {
            throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);
        }
    }

    public static String md5Phone(String phone) {

        String s = DigestUtil.md5Hex16(phone);
        return s;
    }

    public static  <T> T decryptToClass(String priKey, String data, Class<T> clazz) {
        //解密客户信息
        String json = RSAUtils.privateDecrypt(data, priKey);
        log.info("解密后的：" + json);
        return JSON.parseObject(json, clazz);
    }
    //信用派
    public static String encryptData(String data, PublicKey publicKey) {
        try {
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            byte[] dataToEncrypt = data.getBytes("utf-8");
            byte[] encryptedData = cipher.doFinal(dataToEncrypt);
            String encryptString = Base64.encodeBase64String(encryptedData);
            return encryptString;
        } catch (Exception e) {
            throw new RuntimeException("加密失败");
        }
    }

    public static void main(String[] args) {
//        String data = "{\n" +
//                "    \"age\": 22,\n" +
//                "    \"car\": 1,\n" +
//                "    \"channelCode\": \"hrsk\",\n" +
//                "    \"city\": \"南京\",\n" +
//                "    \"custName\": \"测试\",\n" +
//                "    \"education\": 1,\n" +
//                "    \"fund\": 0,\n" +
//                "    \"house\": 2,\n" +
//                "    \"insurance\": 1,\n" +
//                "    \"license\": 2,\n" +
//                "    \"loanAmount\": 5,\n" +
//                "    \"loanTerm\": 3,\n" +
//                "    \"loanUse\": 8,\n" +
//                "    \"monthlyIncome\": 4,\n" +
//                "    \"phone\": \"17750000111\",\n" +
//                "    \"profession\": 1,\n" +
//                "    \"sesame\": 1,\n" +
//                "    \"sex\": 0,\n" +
//                "    \"social\": 0\n" +
//                "}";
//        String phone = "13454545644";
//        try {
//            String publicString = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm3yM/KC/9COBR7ettFDjvPjfkKwEC3tG3duQj7YUF+VHB4O3ka/240Yh9AqrXeM0YHHpwO2LwDnWyQY4earMmeX7vBLRyWxVmji6zyWg3zyPPTdNH8z3dyS4FzDqZcnopTGNOlL/a3mnj7IOjZVkjZ1F//pijDIhzJiDi5ELUEzhEztbfDfggHyBkFckWK42RG6QWjRgHsrBr7Lb4CrFVbrAF4+wLic0oa0FmMIJUEr150fjZzOFEvzsN0x3r8OGrJz8yoUnXS7eWBDJ3NohHihuy2ndYrrAH7Z6zIylxjyNqu+3TV8noeRCt1R7Rzw0sFW1OaqTxQZUNP2D8ppCTQIDAQAB";
//            String dataStr = RSAUtils.publicEncrypt(phone, publicString);
//            System.out.println("加密后参数:" + dataStr);
//
//            String privateString = "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCbfIz8oL/0I4FHt620UOO8+N+QrAQLe0bd25CPthQX5UcHg7eRr/bjRiH0Cqtd4zRgcenA7YvAOdbJBjh5qsyZ5fu8EtHJbFWaOLrPJaDfPI89N00fzPd3JLgXMOplyeilMY06Uv9reaePsg6NlWSNnUX/+mKMMiHMmIOLkQtQTOETO1t8N+CAfIGQVyRYrjZEbpBaNGAeysGvstvgKsVVusAXj7AuJzShrQWYwglQSvXnR+NnM4US/Ow3THevw4asnPzKhSddLt5YEMnc2iEeKG7Lad1iusAftnrMjKXGPI2q77dNXyeh5EK3VHtHPDSwVbU5qpPFBlQ0/YPymkJNAgMBAAECggEAP4MGzAKVphICstrSd387J2u5Vo0NMetvLRyM1kcXuhMLioRBLZ9lB70UDW12RmmScXrWctoVZbW6iG6kDv63iH2q8E33p4E7Dj3yAL9sEQPZtrWqKT0+MEqtb7upY16wSxoMiDBy6OHOkiG3VBob8sS1XTRXoNCxgbMiuJXMu0WogHTB9lBfT1eJKFxecirMTLSV3mwJWLkuuQ+eGtqwjxqrtr5Ishwv89kV5zKd8TEK67rbCqrelfdyTJDL6XNzxK76YtfdXO68s0oUFE7HaAnCBVcStofSsEheDbVWvr4JIEwz3fgSv272x6v9pcXoRLCat9poKF6RE4xZTPToeQKBgQDYCl9hGnUW/81SNO+h+Ps3Fd8p6q7tR4QQqR5Yl8NdVwvjI8LhO6qf4qX9ruG2xaMiJKSrtvAzE7iSu/aWf7yhA3w0DeDGigIywe70ZxQq/Cr2omjN1b58JjW7IdfN0eyjmqnqjCYVjo0RXqJoEiVQyiqD3259Q7v33Ozr07X/DwKBgQC4PuqHYwUeigJ5M/2LTOzApRzyAbhwv7okAhn2TSifHh0/+QCWaEZIND13L26gk7sfxz/Bayl4IEwOAssslFkqPqqOidxH/et+JKG3Lhmyg9O3jU8YBvv8cVrRoBQHXdxHy92jf6QN0LCL/0FuGInptSjsJpCfzWeSPVn43bVo4wKBgGYJGB07qruvG2FNQTTF7JiKyvj4L6A/278msP90xj/KggHIfR112dNj77QOKeryE2dn2SKE55OoJ8+d2QwhzoxZogrP1y7ONdWVRO/H032t1fjgohE9d5cZUN1OUaeTE2n9/Wyavs4FotjmUlxG3br3kPbxyzR8jkW14WxMdLO/AoGAIDZPxq/XO0mhuRMP5wTh96RmdSE73+qFXrpHU7qVuYwITSD5q1I7x2/qtyXBz61Eb//j86sKqwfqhCCU6QxesnYWV6leq++G9pzs8RvMPlux7kt0Yp0F/VLMMA4uskMEukC4VVeBlTDBTRipldgQdJ3Js8/iRS4iR30Wd947U0UCgYBR6+sFiuoGvbTwijZy2NCAJ2d3wuuT0GRPrB3EYhkHdGvDd2PLLwK+Npv1hUbQKY2JFWUDAW5Mne/KT4sPazITVrOiemF58K9BnfY3a6dosF3Tet9ggU+6+E5K5RfLKdRVDg0cs1ansSrErsp+m3iMIlx9envAGPs11nvVXFLiHg==";
//            String ss = privateDecrypt(dataStr, privateString);
//            System.out.println("解密后参数:" + ss);
//
//
//        } catch (Exception e) {
//            e.printStackTrace();
//        }

//        RSA rsa = new RSA();
//        String publicKeyBase64 = rsa.getPublicKeyBase64();
//        String privateKeyBase64 = rsa.getPrivateKeyBase64();
//        System.out.println(publicKeyBase64);
//        System.out.println(privateKeyBase64);
    }
}